/****************************************************************************
*					ExtendedPGMStruct
*
*	Description:	This class provides an extended representation 
*					of the structure of Probabilisitc Graphical Model:			
*					- its primary service is to facilitate 
*						working with rates that reflect the expected times 
*						in which variables tend to change their values 
*					
****************************************************************************/

#ifndef _ExtendedPGMStruct_H
#define _ExtendedPGMStruct_H

#include <vector>
#include "DataSet.h"
#include "PGMStruct.h"
#include "Actor.h"
#include "Environment.h"
#include "lbfgs.h"

class SequencingHeuristic;

class ExtendedPGMStruct : public PGMStruct {
  private:

  protected:

	  /* DATA in protected */
	std::vector < std::pair <unsigned int, unsigned int> > indexOfX;

	  // Changes value of a given variable from 1 to 2 and backwards
	inline bool checkVarChange (const unsigned int& varID) const {
		if (varID==1250)
			int klk = 0;
		bool isX; 
		unsigned int i, j;
		getVarIndex (isX, i, j, varID);
		if (i == j)
			return false;
		if (!isX) {
			if (variables[getVarID (false, i, j)] != 2)
				return true;
			else
				return false;
		}
		// checking  Yij and Yji for Xij
		if ((variables[getVarID (false, i, j)] == 2) && (variables[getVarID (false, j, i)] == 2))
			return true;
		return false;
	}
	  
	  // Computes rate for a given ingredients differnces
	inline float computeRate (const std::vector <float>& ingredientDiffs) const {
		float toRet = 0.0;
		for (unsigned int i = 0; i < ingredientDiffs.size(); i++)
			toRet += ingredientDiffs[i] * weights[i];
		return computeRate (toRet); 
	}

	  // Computes rate for a given weighted sum of ingredients
	static inline float computeRate (const float& weightedSum) {
		return std::exp (weightedSum); 
	}

	  // Function to calculate value of the objective and its gradient
	static lbfgsfloatval_t calcValueAndGradient (void *instance, const lbfgsfloatval_t *x, lbfgsfloatval_t *g, const int n, const lbfgsfloatval_t step);

	  // Function to printout progress of the optimization process
	static int printoutProgress (void *instance, const lbfgsfloatval_t *x, const lbfgsfloatval_t *g, const lbfgsfloatval_t fx, const lbfgsfloatval_t xnorm, const lbfgsfloatval_t gnorm, const lbfgsfloatval_t step, int n, int k, int ls );

  public:

	/* Data */
	  // Number of actors
	unsigned int nActors;
	  // Vector containing all actors:
	std::vector <Actor> actors;
	  // Rates for all variables:
	std::vector< std::pair<float, std::vector <float> > > rates;
	  // Object for creating sequences
	SequencingHeuristic* sequencingHeuristic;


	/* Functions */
	  // Standard constructor - empty one
	ExtendedPGMStruct();
      
	  // Standard destructor - empty one
    ~ExtendedPGMStruct();

	  // Initialization of PGM internal values from environment
	int initStructure (Environment& environment);

	  // Initialization of PGM structure
	int initStructure (DataSet& dataSetMain, DataSet& dataSetAttr);

	  // Sets values of variables from a given timestamp of the data set
	int setVarValuesFromData (DataSet& dataSet, const unsigned int& timestamp);

	  // Removes all elements in the structure
	void clear (void);

	  // Returns rate (lambda) for varID and sets all differences in ingredients for a local net of varID when varID change its value and when it is does not
	float computeRate (const unsigned int& varID, std::vector <float>& ingredientDiffs);
	float computeRate (const unsigned int& varID);

	  // Returns ID of a variable that correspond to actors i and j and has a type isX
	inline unsigned int getVarID (const bool& isX, const unsigned int& i, const unsigned int& j) const {
		if (isX) {
			if (i < j)
				return (nActors*nActors + nActors*i + j - i*(i+1)/2);
			else
				return (nActors*nActors + nActors*j + i - j*(j+1)/2);
		}
		return (nActors*j + i); }

	  // Fulfills indexes i and j and variable isX given id of a variable in this->variables
	inline void getVarIndex (bool& isX, unsigned int& i, unsigned int& j, const unsigned int& varID) const {
		isX = (varID >= (nActors*nActors));
		if (isX) {
			i = indexOfX[varID - nActors*nActors].first;
			j = indexOfX[varID - nActors*nActors].second;
		} else { 
			j = varID / nActors;
			i = varID % nActors; } 
	}

	  // Returns ID of a variable that correspond to actors i and j and has a type isX
	inline unsigned int getSquareVarID (const bool& isX, const unsigned int& i, const unsigned int& j) const {
		if (isX)
			return (nActors*nActors + nActors*j + i);
		return (nActors*j + i); }

	  // Fulfills indexes i and j and variable isX given id of a variable in this->variables
	inline void getSquareVarIndex (bool& isX, unsigned int& i, unsigned int& j, const unsigned int& varID) const {
		isX = (varID >= (nActors*nActors));
		if (isX)
			j = (varID - nActors*nActors) / nActors;
		else
			j = varID / nActors;
		i = varID % nActors;
	}

	  // Changes value of a given variable from 1 to 2 and backwards
	inline void changeVarValue (const unsigned int& varID) {
		if (variables[varID] != 2)
			variables[varID] = 2;
		else 
			variables[varID] = 1; 
	}
	
	  // Initialize rate values for all variables
	int computeRates();

	  // Returns revised rates for all variables enduring changes after a change in a single variable
	int reviseRates (std::map< unsigned int, std::pair<float, std::vector<float>> >& revisedRates, std::vector<unsigned int>& varIDs);
	  // same function, returns:			  varID			newRate								size of the vector 'revisedRates'	initialy changed variable
	int reviseRates (std::vector < std::pair <unsigned int, float> >& revisedRates, unsigned int& revRatesSize, const unsigned int& varID);

	  // Updates revised rates for all variables enduring changes after a change in a single variable and puts changes to rates, stored in this class
	float updateRates (const unsigned int& varID, std::vector <float>& TERM2);

	  // Update network structure-dependent actor attributes (indegree, outdegree, degree) after a change in a single variable
	int updateAttributes (const unsigned int& varID);

	  // learns weights of the ingredients given data set
	int learn (DataSet& dataSet);
	
}; // end of class

// Structure to pass dataSet and other structures to calcValueAndGradient function as one pointer
struct RequiredStructures {
	ExtendedPGMStruct* extPGMStruct;
	DataSet* dataSet;
}; // end of struct

#endif // _ExtendedPGMStruct_H
